package com.pkk.components.common.config.properties;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.springframework.core.env.Environment;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

/**
 * @description: 解析表达式
 * @author: peikunkun
 * @create: 2019-07-27 12:32
 **/
public class CustomerPropertyPlaceholderHelper {

  private static final Map<String, String> wellKnownSimplePrefixes = new HashMap(4);

  /*前缀*/
  private final String placeholderPrefix;
  /*后缀*/
  private final String placeholderSuffix;
  private final String simplePrefix;

  /*值得分隔符*/
  @Nullable
  private final String valueSeparator;
  private final boolean ignoreUnresolvablePlaceholders;

  public CustomerPropertyPlaceholderHelper(String placeholderPrefix, String placeholderSuffix) {
    this(placeholderPrefix, placeholderSuffix, (String) null, true);
  }

  public CustomerPropertyPlaceholderHelper(String placeholderPrefix, String placeholderSuffix, String valueSeparator,
      boolean ignoreUnresolvablePlaceholders) {
    Assert.notNull(placeholderPrefix, "'placeholderPrefix' must not be null");
    Assert.notNull(placeholderSuffix, "'placeholderSuffix' must not be null");
    this.placeholderPrefix = placeholderPrefix;
    this.placeholderSuffix = placeholderSuffix;
    String simplePrefixForSuffix = (String) wellKnownSimplePrefixes.get(this.placeholderSuffix);
    if (simplePrefixForSuffix != null && this.placeholderPrefix.endsWith(simplePrefixForSuffix)) {
      this.simplePrefix = simplePrefixForSuffix;
    } else {
      this.simplePrefix = this.placeholderPrefix;
    }

    this.valueSeparator = valueSeparator;
    this.ignoreUnresolvablePlaceholders = ignoreUnresolvablePlaceholders;
  }

  public String resolvePlaceholder(Environment environment, String key) {
    return this.parseStringValue(environment, key, new HashSet());
  }

  /**
   * @Description: 解析字符串
   * @Param: [environment, value, visitedPlaceholders]
   * @return: java.lang.String
   * @Author: peikunkun
   * @Date: 2019/7/27 0027 下午 12:34
   */
  protected String parseStringValue(Environment environment, String value, Set<String> visitedPlaceholders) {
    StringBuilder result = new StringBuilder(value);
    int startIndex = value.indexOf(this.placeholderPrefix);

    while (startIndex != -1) {
      int endIndex = this.findPlaceholderEndIndex(result, startIndex);
      if (endIndex != -1) {
        String placeholder = result.substring(startIndex + this.placeholderPrefix.length(), endIndex);
        String originalPlaceholder = placeholder;
        if (!visitedPlaceholders.add(placeholder)) {
          throw new IllegalArgumentException(
              "Circular placeholder reference '" + placeholder + "' in property definitions");
        }

        placeholder = this.parseStringValue(environment, placeholder, visitedPlaceholders);
        String propVal = environment.getProperty(placeholder);
        if (propVal == null && this.valueSeparator != null) {
          int separatorIndex = placeholder.indexOf(this.valueSeparator);
          if (separatorIndex != -1) {
            placeholder.substring(0, separatorIndex);
            String defaultValue = placeholder.substring(separatorIndex + this.valueSeparator.length());
            propVal = environment.getProperty(placeholder);
            if (propVal == null) {
              propVal = defaultValue;
            }
          }
        }

        if (propVal != null) {
          propVal = this.parseStringValue(environment, propVal, visitedPlaceholders);
          result.replace(startIndex, endIndex + this.placeholderSuffix.length(), propVal);
          startIndex = result.indexOf(this.placeholderPrefix, startIndex + propVal.length());
        } else {
          if (!this.ignoreUnresolvablePlaceholders) {
            throw new IllegalArgumentException(
                "Could not resolve placeholder '" + placeholder + "' in value \"" + value + "\"");
          }

          startIndex = result.indexOf(this.placeholderPrefix, endIndex + this.placeholderSuffix.length());
        }

        visitedPlaceholders.remove(originalPlaceholder);
      } else {
        startIndex = -1;
      }
    }

    return result.toString();
  }

  private int findPlaceholderEndIndex(CharSequence buf, int startIndex) {
    int index = startIndex + this.placeholderPrefix.length();
    int withinNestedPlaceholder = 0;

    while (index < buf.length()) {
      if (StringUtils.substringMatch(buf, index, this.placeholderSuffix)) {
        if (withinNestedPlaceholder <= 0) {
          return index;
        }

        --withinNestedPlaceholder;
        index += this.placeholderSuffix.length();
      } else if (StringUtils.substringMatch(buf, index, this.simplePrefix)) {
        ++withinNestedPlaceholder;
        index += this.simplePrefix.length();
      } else {
        ++index;
      }
    }

    return -1;
  }

  static {
    wellKnownSimplePrefixes.put("}", "{");
    wellKnownSimplePrefixes.put("]", "[");
    wellKnownSimplePrefixes.put(")", "(");
  }
}
